/*
-----------------------------------------------------------------------------
Filename:    BasicTutorial2.cpp
-----------------------------------------------------------------------------
 
This source file is part of the
   ___                 __    __ _ _    _ 
  /___\__ _ _ __ ___  / / /\ \ (_) | _(_)
 //  // _` | '__/ _ \ \ \/  \/ / | |/ / |
/ \_// (_| | | |  __/  \  /\  /| |   <| |
\___/ \__, |_|  \___|   \/  \/ |_|_|\_\_|
      |___/                              
	  Tutorial Framework
	  http://www.ogre3d.org/tikiwiki/
-----------------------------------------------------------------------------
*/
 
#include "BasicTutorial2.h"
#include "math.h"
 

#define COMPLEXITY 64 		// watch out - number of polys is 2*ACCURACY*ACCURACY !
#define PLANE_SIZE 3000.0f

//-------------------------------------------------------------------------------------
BasicTutorial2::BasicTutorial2(void)
{
}
//-------------------------------------------------------------------------------------
BasicTutorial2::~BasicTutorial2(void)
{
}
//-------------------------------------------------------------------------------------
void BasicTutorial2::createCamera(void)
{
    // create the camera
    mCamera = mSceneMgr->createCamera("PlayerCam");
    // set its position, direction  
    mCamera->setPosition(Ogre::Vector3(500,200,0));
    mCamera->lookAt(Ogre::Vector3(0,0,0));
    // set the near clip distance
    mCamera->setNearClipDistance(5);
 
    mCameraMan = new OgreBites::SdkCameraMan(mCamera);   // create a default camera controller
}
//-------------------------------------------------------------------------------------
void BasicTutorial2::createViewports(void)
{
    // Create one viewport, entire window
    Ogre::Viewport* vp = mWindow->addViewport(mCamera);
    vp->setBackgroundColour(Ogre::ColourValue(0,0,0));
    // Alter the camera aspect ratio to match the viewport
    mCamera->setAspectRatio(Ogre::Real(vp->getActualWidth()) / Ogre::Real(vp->getActualHeight()));    
}
//-------------------------------------------------------------------------------------

void BasicTutorial2::animateBall(Ogre::Real timeSinceLastFrame)
	{

		// sine track? :)
		static double sines[4] = {0,100,200,300};
		static const double adds[4] = {0.3,-1.6,1.1,0.5};
      static Ogre::Vector3 oldPos = Ogre::Vector3::UNIT_Z;
		for(int i=0;i<4;i++) {
			sines[i]+=adds[i]*timeSinceLastFrame;
		}
      Ogre::Real tx = ((sin(sines[0]) + sin(sines[1])) / 4 + 0.5 ) * (float)(COMPLEXITY-2) + 1 ;
		Ogre::Real ty = ((sin(sines[2]) + sin(sines[3])) / 4 + 0.5 ) * (float)(COMPLEXITY-2) + 1 ;
		//waterMesh->push(tx,ty, -headDepth);
		Ogre::Real step = PLANE_SIZE / COMPLEXITY ;
		ballNode->resetToInitialState();
		ballNode->scale(3,3,3);
      Ogre::Vector3 newPos = Ogre::Vector3(step*tx, headDepth, step*ty);
		Ogre::Vector3 diffPos = newPos - oldPos ;
      Ogre::Quaternion ballRotation = Ogre::Vector3::UNIT_Z.getRotationTo(diffPos);
		oldPos = newPos ;
		ballNode->translate(newPos);
		ballNode->rotate(ballRotation);
	}
//-------------------------------------------------------------------------------------

void BasicTutorial2::createScene(void)
{
    numberOfLifes = 5;

    mSceneMgr->setAmbientLight(Ogre::ColourValue(0, 0, 0));
    mSceneMgr->setShadowTechnique(Ogre::SHADOWTYPE_STENCIL_ADDITIVE);
 
    Ogre::Entity* entNinja = mSceneMgr->createEntity("Ninja", "ninja.mesh");
    entNinja->setCastShadows(true);
    Ogre::SceneNode* ninjaNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("NinjaNode", Ogre::Vector3( 500, 0, 0 ));
    ninjaNode->rotate(Ogre::Vector3(0,1,0),Ogre::Radian(2));
    ninjaNode->attachObject(entNinja);

    Ogre::Entity* entNinja2 = mSceneMgr->createEntity("Ninja2", "ninja.mesh");
    entNinja2->setCastShadows(true);
    Ogre::SceneNode* ninjaNode2 = mSceneMgr->getRootSceneNode()->createChildSceneNode("NinjaNode2", Ogre::Vector3( -500, 0, 0 ));
    ninjaNode2->rotate(Ogre::Vector3(0,1,0),Ogre::Radian(-2));
    ninjaNode2->attachObject(entNinja2);

    Ogre::Entity* entNinja3 = mSceneMgr->createEntity("Ninja3", "ninja.mesh");
    entNinja3->setCastShadows(true);
    Ogre::SceneNode* ninjaNode3 = mSceneMgr->getRootSceneNode()->createChildSceneNode("NinjaNode3", Ogre::Vector3( -500, 0, 200 ));
    ninjaNode3->rotate(Ogre::Vector3(0,1,0),Ogre::Radian(-2)); 
    ninjaNode3->attachObject(entNinja3);

    Ogre::Entity* entNinja4 = mSceneMgr->createEntity("Ninja4", "ninja.mesh");
    entNinja4->setCastShadows(true);   
    Ogre::SceneNode* ninjaNode4 = mSceneMgr->getRootSceneNode()->createChildSceneNode("NinjaNode4", Ogre::Vector3( 500, 0, 200 ));
    ninjaNode4->rotate(Ogre::Vector3(0,1,0),Ogre::Radian(2));
    ninjaNode4->attachObject(entNinja4);
   
    /*
     * Creation of the ball
     */
    
    ball = new Ball( );
    ball->setRadius(10);
    ball->setPosition(Ogre::Vector3(0,ball->getRadius( ) + 200,0));
    ball->setmovementVector(Ogre::Vector3(1,0,0));

    createSphere("mySphereMesh", ball->getRadius( ), 64, 64);
    Ogre::Entity* entBall = mSceneMgr->createEntity("Ball", "mySphereMesh");
    ballNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("BallNode");
    //entBall->setMaterialName("Examples/Droplet");
    ballNode->translate(ball->getPosition( ));
    ballNode->attachObject(entBall);   
    
    /*
     * End of the ball creation
     */
    Ogre::Plane plane(Ogre::Vector3::UNIT_Y, 0);
 
    Ogre::MeshManager::getSingleton().createPlane("ground", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
    plane, 1500, 1500, 20, 20, true, 1, 5, 5, Ogre::Vector3::UNIT_Z);
 
    Ogre::Entity* entGround = mSceneMgr->createEntity("GroundEntity", "ground");
    mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(entGround);
    
    
    entGround->setMaterialName("Examples/Rockwall");
    entGround->setCastShadows(false);
 
    Ogre::Light* pointLight = mSceneMgr->createLight("pointLight");
    pointLight->setType(Ogre::Light::LT_POINT);
    pointLight->setPosition(Ogre::Vector3(0, 150, 250));
 
    pointLight->setDiffuseColour(1.0, 0.0, 0.0);
    pointLight->setSpecularColour(1.0, 0.0, 0.0);
 
    Ogre::Light* directionalLight = mSceneMgr->createLight("directionalLight");
    directionalLight->setType(Ogre::Light::LT_DIRECTIONAL);
    directionalLight->setDiffuseColour(Ogre::ColourValue(.25, .25, 0));
    directionalLight->setSpecularColour(Ogre::ColourValue(.25, .25, 0));
 
    directionalLight->setDirection(Ogre::Vector3( 0, -1, 1 )); 
 
    Ogre::Light* spotLight = mSceneMgr->createLight("spotLight");
    spotLight->setType(Ogre::Light::LT_SPOTLIGHT);
    spotLight->setDiffuseColour(0, 0, 1.0);
    spotLight->setSpecularColour(0, 0, 1.0);
 
    spotLight->setDirection(-1, -1, 0);
    spotLight->setPosition(Ogre::Vector3(300, 300, 0));
 
    spotLight->setSpotlightRange(Ogre::Degree(35), Ogre::Degree(50));
    timeoutDelay = -10.0f;
    modMov = 0.1f;
};

void BasicTutorial2::createSphere(const std::string& strName, const float r, const int nRings , const int nSegments )
 {
    Ogre::MeshPtr pSphere = Ogre::MeshManager::getSingleton().createManual(strName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
     Ogre::SubMesh *pSphereVertex = pSphere->createSubMesh();
 
     pSphere->sharedVertexData = new Ogre::VertexData();
     Ogre::VertexData* vertexData = pSphere->sharedVertexData;
 
     // define the vertex format
     Ogre::VertexDeclaration* vertexDecl = vertexData->vertexDeclaration;
     size_t currOffset = 0;
     // positions
     vertexDecl->addElement(0, currOffset, Ogre::VET_FLOAT3, Ogre::VES_POSITION);
     currOffset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);
     // normals
     vertexDecl->addElement(0, currOffset, Ogre::VET_FLOAT3, Ogre::VES_NORMAL);
     currOffset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);
     // two dimensional texture coordinates
     vertexDecl->addElement(0, currOffset, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, 0);
     currOffset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2);
 
     // allocate the vertex buffer
     vertexData->vertexCount = (nRings + 1) * (nSegments+1);
     Ogre::HardwareVertexBufferSharedPtr vBuf = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer(vertexDecl->getVertexSize(0), vertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, false);
     Ogre::VertexBufferBinding* binding = vertexData->vertexBufferBinding;
     binding->setBinding(0, vBuf);
     float* pVertex = static_cast<float*>(vBuf->lock(Ogre::HardwareBuffer::HBL_DISCARD));
 
     // allocate index buffer
     pSphereVertex->indexData->indexCount = 6 * nRings * (nSegments + 1);
     pSphereVertex->indexData->indexBuffer = Ogre::HardwareBufferManager::getSingleton().createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, pSphereVertex->indexData->indexCount, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, false);
     Ogre::HardwareIndexBufferSharedPtr iBuf = pSphereVertex->indexData->indexBuffer;
     unsigned short* pIndices = static_cast<unsigned short*>(iBuf->lock(Ogre::HardwareBuffer::HBL_DISCARD));
     
     float fDeltaRingAngle = (Ogre::Math::PI / nRings);
     float fDeltaSegAngle = (2 *  Ogre::Math::PI / nSegments);
     unsigned short wVerticeIndex = 0 ;
 
     // Generate the group of rings for the sphere
     for( int ring = 0; ring <= nRings; ring++ ) {
         float r0 = r * sinf (ring * fDeltaRingAngle);
         float y0 = r * cosf (ring * fDeltaRingAngle);
 
         // Generate the group of segments for the current ring
         for(int seg = 0; seg <= nSegments; seg++) {
             float x0 = r0 * sinf(seg * fDeltaSegAngle);
             float z0 = r0 * cosf(seg * fDeltaSegAngle);
 
             // Add one vertex to the strip which makes up the sphere
             *pVertex++ = x0;
             *pVertex++ = y0;
             *pVertex++ = z0;
 
             Ogre::Vector3 vNormal = Ogre::Vector3(x0, y0, z0).normalisedCopy();
             *pVertex++ = vNormal.x;
             *pVertex++ = vNormal.y;
             *pVertex++ = vNormal.z;
 
             *pVertex++ = (float) seg / (float) nSegments;
             *pVertex++ = (float) ring / (float) nRings;
 
             if (ring != nRings) {
                                // each vertex (except the last) has six indices pointing to it
                 *pIndices++ = wVerticeIndex + nSegments + 1;
                 *pIndices++ = wVerticeIndex;               
                 *pIndices++ = wVerticeIndex + nSegments;
                 *pIndices++ = wVerticeIndex + nSegments + 1;
                 *pIndices++ = wVerticeIndex + 1;
                 *pIndices++ = wVerticeIndex;
                 wVerticeIndex ++;
             }
         }; // end for seg
     } // end for ring
 
     // Unlock
     vBuf->unlock();
     iBuf->unlock();
     // Generate face list
     pSphereVertex->useSharedVertices = true;
 
     // the original code was missing this line:
     pSphere->_setBounds( Ogre::AxisAlignedBox( Ogre::Vector3(-r, -r, -r), Ogre::Vector3(r, r, r) ), false );
     pSphere->_setBoundingSphereRadius(r);
         // this line makes clear the mesh is loaded (avoids memory leaks)
         pSphere->load();
  }

bool BasicTutorial2::frameRenderingQueued(const Ogre::FrameEvent& evt)
    {
		 bool ret = BaseApplication::frameRenderingQueued(evt);
       
       if (timeoutDelay>=8)
			modMov = -0.1f;
       if (timeoutDelay<=-8)
         modMov = 0.1f; 

       
       //timeoutDelay-=evt.timeSinceLastFrame ;
		 timeoutDelay+=modMov ;
		
       
       ball->setPosition(ball->getmovementVector());
       ballNode->translate(ball->getPosition( ));
		 
       // animateBall(evt.timeSinceLastFrame);
       
       if ( this->collisionExists( mCamera->getRealPosition(), ballNode->getPosition())) {
            mDetailsPanel->setParamValue(11, "Muerto" );
            numberOfLifes -= 1;
            ballNode->setPosition(Ogre::Vector3(0,ball->getRadius( ) + 200,0));
            ball->setPosition(Ogre::Vector3(0,ball->getRadius( ) + 200,0));
       
       }
       return ret;
    }
bool BasicTutorial2::collisionExists(Ogre::Vector3 me, Ogre::Vector3 ball) {
   return ( this->ball->getRadius() >  sqrt(pow( (me.x -ball.x),2) + pow( (me.y -ball.y),2) + pow( (me.z -ball.z),2)     ));
}

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#endif
 
#ifdef __cplusplus
	extern "C" {
#endif
 
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
		INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
#else
		int main(int argc, char *argv[])
#endif
		{
			// Create application object
			BasicTutorial2 app;
 
			try {
				app.go();
			} catch( Ogre::Exception& e ) {
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
				MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
				std::cerr << "An exception has occured: " <<
					e.getFullDescription().c_str() << std::endl;
#endif
			}
 
			return 0;
		}
 
#ifdef __cplusplus
	}
#endif